package com.ccl.iot;

import android.util.Log;

import com.ccl.iot.object.IOTInfoWithValue;
import com.ccl.iot.privates.Invoker;
import com.ccl.iot.privates.ListMap;

import java.util.ArrayList;

public abstract class IOTInfoSynchro extends IOTInfoMonitor {
    protected interface IOTInfoProvider {
        public long getOwner();

        public String getValue(String aName);

        public int getVersion();

        public long getCount();

        public boolean updateList();

        public int getFirst(IOTInfoWithValue aInfo);

        public int getNext(IOTInfoWithValue aInfo);

        public boolean addInfo(String aName, String aValue);

        public boolean setInfo(String aName, String aValue);

        public boolean removeInfo(String aName);
    }

    public interface IOTInfoManager {
        public void updateInfo(IOTInfoSynchro aSynchro);
    }

    /*
     * 	Non-static functions
     */
    private int iRetriveCount = 0;
    private IOTScanner<IOTInfoWithValue> iScanner = null;
    private IOTInfoProvider iInfoProvider = null;
    private IOTInfoManager iInfoManager = null;

    private long iOwner = 0;
    private ListMap<String, IOTInfoWithValue> iInfos = null;

    protected int iLastVersion = -1;
    private int iTmpVersion = 0;

    IOTInfoSynchro(IOTInfoProvider aInfoProvoder) {
        iScanner = new IOTScanner<IOTInfoWithValue>(null);
        iInfoProvider = aInfoProvoder;
    }

    public void setInfoManager(IOTInfoManager aManager) {
        iInfoManager = aManager;
    }

    public boolean isRetriving() {
        if (iScanner != null) {
            return iScanner.isScanning();
        } else {
            return false;
        }
    }

    public int getRetriveCount() {
        return iRetriveCount;
    }

    public boolean startRetrive() {
        if (iScanner != null && iInfoProvider != null) {
            iTmpVersion = iInfoProvider.getVersion();
            if (iTmpVersion != iLastVersion) {
                Log.e("version", "start retrive iTmpVersion: "+iTmpVersion+", iLastVersion: "+iLastVersion);
                if (iInfoProvider.updateList()) {
                    final ListMap<String, IOTInfoWithValue> tOldList = iInfos;
                    final IOTInfoWithValue tInfo = new IOTInfoWithValue();

                    iInfos = new ListMap<String, IOTInfoWithValue>();

                    iOwner = iInfoProvider.getOwner();

                    return iScanner.startScan(new IOTScanner.ScanImpl<IOTInfoWithValue>() {
                        int result = -1;

                        long count = 0;

                        private IOTInfoWithValue newInfo(IOTInfoWithValue aInfo) {
                            if (aInfo != null) {
                                count++;
                                boolean tNew = true;
                                IOTInfoWithValue tInfo = null;

                                if (tOldList != null) {
                                    tInfo = tOldList.getValue(aInfo.getName());
                                }

                                if (tInfo == null) {
                                    tInfo = aInfo.clone();
                                    tInfo.setValue(aInfo.getValue());
                                } else {
                                    String tOldValue = tInfo.getValue();
                                    String tNewValue = aInfo.getValue();
                                    tInfo.setValue(tNewValue);
                                    tNew = false;

                                    synchronized (tOldList) {
                                        tOldList.remove(tInfo.getName());
                                    }

                                    if ((tOldValue == null && tNewValue != null) ||
                                            tOldValue != null && tOldValue.equals(tNewValue) == false) {
                                        onInfoChange(tInfo, false);
                                    }
                                }
                                if (iInfos.add(tInfo.getName(), tInfo)) {
                                    if (tNew) {
                                        onInfoChange(tInfo, true);
                                    }

                                    return tInfo;
                                }
                            }

                            return null;
                        }

                        @Override
                        public IOTInfoWithValue scanFirst() {
                            if ((result = iInfoProvider.getFirst(tInfo)) == 0) {
//                                getPublicUserInfoList
                                return newInfo(tInfo);
                            } else {
                                return null;
                            }
                        }

                        @Override
                        public IOTInfoWithValue scanNext() {
                            if ((result = iInfoProvider.getNext(tInfo)) == 0) {
                                IOTInfoWithValue iotInfoWithValue = newInfo(tInfo);
                                Log.e("setInfoValue", "scanNext: "+iotInfoWithValue);
                                return iotInfoWithValue;
                            } else {
                                return null;
                            }
                        }

                        @Override
                        public void scanStart() {
                            onInfoRetriveStart();
                        }

                        @Override
                        public void scanDone() {
                            iRetriveCount++;

                            if (tOldList != null) {
                                synchronized (tOldList) {
                                    for (int i = 0, tCount = tOldList.size(); i < tCount; i++) {
                                        onInfoRemoved(tOldList.getValueAt(i));
                                    }
                                }
                            }
                            if (iInfos != null && iInfos.size() > 0 && count == iInfoProvider.getCount()) {
                                iLastVersion = iTmpVersion;
//                                DeviceManager.GetDefaultManager().refresh(DeviceManager.FindDevice(iInfoProvider.getOwner()));
                            }
                            onInfoRetriveStop();
                            count = 0;
                        }
                    }, null);
                }
            } else {
                onInfoRetriveStart();
                onInfoRetriveStop();
//                DeviceManager.GetDefaultManager().refresh(DeviceManager.FindDevice(iInfoProvider.getOwner()));
                return true;
            }

        }

        return false;
    }

    public boolean stopRetrive() {
        if (iScanner != null) {
            return iScanner.stopScan();
        } else {
            return false;
        }
    }

    public ArrayList<IOTInfoWithValue> getAllInfo() {
        final ListMap<String, IOTInfoWithValue> tInfos = iInfos;

        if (tInfos != null) {
            return tInfos.getValues();
        } else {
            return null;
        }
    }

    public String getInfoValue(String aName) {
        final ListMap<String, IOTInfoWithValue> tInfos = iInfos;
        if (tInfos != null && tInfos.size() > 0 && iInfoProvider != null) {
            if (iInfoProvider.getOwner() != iOwner) {
                if (iInfoManager != null) {
                    iInfoManager.updateInfo(IOTInfoSynchro.this);
                }
//                return iInfoProvider.getValue(aName);
            } else {
                IOTInfoWithValue tInfo;

                synchronized (tInfos) {
                    if ((tInfo = tInfos.getValue(aName)) != null) {
                        return tInfo.getValue();
                    }
                }
            }
        }

        return null;
    }

    public String getInfoValue(String aName, String aDefaultValue) {
        String tValue = getInfoValue(aName);
        if (tValue == null) {
            return aDefaultValue;
        } else {
            return tValue;
        }
    }

    /*
     * 	Commit and update part
     */
    private IOTInfoLogger iChangeLog = new IOTInfoLogger();

    protected void updateInfo() {
        if (iInfoManager != null) {
            iInfoManager.updateInfo(IOTInfoSynchro.this);
        }
    }

    private Invoker iCommiter = new Invoker("IOT info commit", 500, new Invoker.InvokeListener() {
        @Override
        public void onInvoke() {
            synchronized (iCommiter) {
                if (iInfoProvider != null && iChangeLog != null) {
                    if (iChangeLog.isChanged()) {
                        int tIndex = 0;
                        int tChanges = 0;
                        ListMap<String, String> tRemoved = iChangeLog.getRemoved();
                        ListMap<String, String> tAdded = iChangeLog.getAdded();
                        ListMap<String, String> tChanged = iChangeLog.getChanged();

                        if (tRemoved != null) {
                            tIndex = 0;
                            synchronized (tRemoved) {
                                for (int i = 0, tCount = tRemoved.size(); i < tCount; i++) {
                                    if (iInfoProvider.removeInfo(tRemoved.getKeyAt(tIndex))) {
                                        tRemoved.removeAt(tIndex);
                                        tChanges++;
                                    } else {
                                        tIndex++;
                                    }
                                }
                            }
                        }

                        if (tAdded != null) {
                            tIndex = 0;
                            synchronized (tAdded) {
                                for (int i = 0, tCount = tAdded.size(); i < tCount; i++) {
                                    if (iInfoProvider.addInfo(tAdded.getKeyAt(tIndex), tAdded.getValueAt(tIndex))) {
                                        tAdded.removeAt(tIndex);
                                        tChanges++;
                                    } else {
                                        tIndex++;
                                    }
                                }
                            }
                        }

                        if (tChanged != null) {
                            tIndex = 0;
                            synchronized (tChanged) {
                                for (int i = 0, tCount = tChanged.size(); i < tCount; i++) {
                                    if (iInfoProvider.setInfo(tChanged.getKeyAt(tIndex), tChanged.getValueAt(tIndex))) {
                                        tChanged.removeAt(tIndex);
                                        tChanges++;
                                    } else {
                                        tIndex++;
                                    }
                                }
                            }
                        }

                        if (tChanges > 0) {
                            updateInfo();
                        }
                    }
                }
            }
        }
    });

    @Override
    protected void onInfoChange(IOTInfoWithValue aInfo, boolean aNew) {
        synchronized (iCommiter) {
            if (iScanner.isScanning() == false) {
                if (iChangeLog != null) {
                    if (aNew) {
                        iChangeLog.add(aInfo);
                    } else {
                        iChangeLog.change(aInfo);
                    }
                    iCommiter.invoke();
                }
            }
        }

        super.onInfoChange(aInfo, aNew);
    }

    @Override
    protected void onInfoRemoved(IOTInfoWithValue aInfo) {
        synchronized (iCommiter) {
            if (iScanner.isScanning() == false) {
                if (iChangeLog != null) {
                    iChangeLog.remove(aInfo);
                    iCommiter.invoke();
                }
            }
        }

        super.onInfoRemoved(aInfo);
    }

    public boolean setInfoValue(String aName, String aValue) {
        if (iInfoProvider != null) {
            String tValue = getInfoValue(aName);

            if (tValue == null) {
                IOTInfoWithValue tInfo = new IOTInfoWithValue(0, aName, aValue);

                if (iInfos == null) {
                    iInfos = new ListMap<String, IOTInfoWithValue>();
                }

                if (iInfos.add(aName, tInfo)) {
                    onInfoChange(tInfo, true);

                    return true;
                }
            } else {
                final ListMap<String, IOTInfoWithValue> tInfos = iInfos;

                if (tInfos != null) {
                    IOTInfoWithValue tInfo;

                    synchronized (tInfos) {
                        tInfo = tInfos.getValue(aName);
                    }
                    if (tInfo != null) {
                        if (tInfo.setValue(aValue)) {
                            onInfoChange(tInfo, false);
                            return true;
                        }
                    }
                }
            }
        }

        return false;
    }

    public boolean removeInfo(String aName) {
        boolean tRet = false;
        final ListMap<String, IOTInfoWithValue> tInfos = iInfos;

        if (tInfos != null && tInfos.size() > 0) {
            IOTInfoWithValue tInfo;

            synchronized (tInfos) {
                tInfo = tInfos.getValue(aName);

                if (tInfo != null) {
                    tRet = tInfos.remove(aName);
                }
            }

            if (tRet) {
                onInfoRemoved(tInfo);
            }
        }

        return tRet;
    }

    public int clean(boolean aCacheOnly) {
        int tRemoved = 0;
        final ListMap<String, IOTInfoWithValue> tInfos = iInfos;

        if (tInfos != null && tInfos.size() > 0) {
            ArrayList<String> tNames = iInfos.getKeys();

            if (tNames != null) {
                String tName;

                while (tNames.size() > 0) {
                    synchronized (tInfos) {
                        tName = tNames.get(0);
                    }

                    if (tName != null) {
                        if (aCacheOnly) {
                            IOTInfoWithValue tInfo;

                            synchronized (tInfos) {
                                tInfo = tInfos.getValue(tName);

                                if (tInfos.remove(tName)) {
                                    tRemoved++;
                                } else {
                                    break;
                                }
                            }

                            onInfoRemoved(tInfo);
                        } else {
                            if (removeInfo(tName)) {
                                tRemoved++;
                            } else {
                                break;
                            }
                        }
                    } else {
                        break;
                    }
                }
            }
        }

        return tRemoved;
    }
}
